use core::fmt::{Display, Formatter};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use crate::error::Error;
use crate::modeling::Value;

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]

pub struct CommandTask {
    pub cmd: String,
    pub args: Option<Vec<String>>,
}

///```json
///{
///    "env": {"",""},
///    "current":"",
///    "before": [ {"cmd":"COPY","args":["",""]} ],
///    "script": ["ls","cd /"],
///    "after": [ {"cmd":"COPY","args":["",""]} ]
///}
///```
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]

pub struct ShellRequestMessage {
    pub env: HashMap<String, String>,
    pub current: Option<String>,
    pub before: Option<Vec<CommandTask>>,
    pub script: Vec<Vec<String>>,
    pub after: Option<Vec<CommandTask>>,
}

///```json
///{
///    "env": {"",""},
///    "current":"",
///    "before": [ {"cmd":"COPY","args":["",""]} ],
///    "script": string,
///    "after": [ {"cmd":"COPY","args":["",""]} ]
///}
///```
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]

pub struct BatchScriptRequestMessage {
    pub env: HashMap<String, String>,
    pub current: Option<String>,
    pub before: Option<Vec<CommandTask>>,
    pub script: String,
    pub after: Option<Vec<CommandTask>>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct ShellRequestOptions {
    pub remote: Option<bool>,
    pub model: Option<String>,
    pub extensions: Option<HashMap<String, String>>,
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
pub struct ShellResponseMessage {
    pub options: HashMap<String, String>,
}

impl ShellResponseMessage {
    pub fn new() -> anyhow::Result<Self> {
        Ok(Self {
            options: HashMap::new(),
        })
    }
}

impl TryFrom<Value> for ShellRequestMessage {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let serde_value: serde_json::Value = value.try_into()?;
        let message = serde_json::from_value(serde_value)?;
        Ok(message)
    }
}

impl TryInto<Value> for ShellRequestMessage {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Value::try_from(serde_json::json!(self))
    }
}

impl ShellRequestOptions {
    pub fn new() -> anyhow::Result<Self> {
        Ok(Self {
            remote: None,
            model: None,
            extensions: None,
        })
    }
}
